/*============================================================
 Create Time:			2021-09-29 11:55:45
<<<<<<< HEAD
 Last modify:			2021-10-11 12:31:55
=======
 Last modify:			2021-10-17 21:10:42
>>>>>>> b4624289e061d41d21da91474e13a6ff44ac13f6
 Writer:				Wenhao	1795902848@qq.com
 File Name:				main.c
 File Type:				C Source File
 Tool:					Mac -- vim & gcc, gdb
 Information:			第二次嵌入式实验，用按键来控制LED灯。
============================================================*/
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>

#define LED_ON  0
#define LED_OFF  1

#define BTN_PRESSED 1
#define BTN_LOOSE 0

#define or ||
#define and &&

#define NUM_LEDS 4
#define NUM_BTNS 2


int gi32_led_status = 0;

int* get_handles(char* device_base_path, int count)
{
	/*
	 * 获取设备的句柄。所有设备的打开方式都是：可读可写。
	   @createTime: 2021-10-01 05:20:18
	   @arg: char* device_base_path 待读取的文件名（设备名、设备路径）
	   @arg:  int count （由于本实验中设备的名字是相邻的，所以可以循环count次，来获取count个设备的句柄）
	   @rtn: 返回获取的count个句柄。
	*/
	int* handles = (int*)(malloc(sizeof(int) * count));
	int str_len = strlen(device_base_path);
	for(int i = 0; i < count; i++) {
		device_base_path[str_len - 1] = '0' + i;
		handles[i] = open(device_base_path, O_RDWR);
	}
	return handles;
}

void close_handles(int* handles, int len)
{
	/*
	 * 关闭设备的句柄
	   @createTime: 2021-10-01 05:20:27
	   @arg: int* handles 存放文件句柄的数组
	   @arg:  int len 数组的长度
	   @rtn: NULL
*/
	for(int i = 0; i < len; i++)
		close(handles[i]);
}

void read_status(int* handles, char* status, int len)
{
	/*
	 * 操作设备句柄，读句柄对应设备中的数据。
	   @createTime: 2021-10-01 05:20:30
	   @arg: int* handles 待操作的设备句柄
	   @arg:  char* status 从句柄中读取的数据要存放的位置
	   @arg:  int len 句柄数组的长度，也是status数组的长
	   @rtn: NULL
*/
	for(int i = 0; i < len; i++)
		read(handles[i], status + i, 1);
}

void write_status(int* handles, char* status, int len)
{
	/*
	 * 向句柄对应设备写入数据。写入的是status中的数据，长度为len个字节。
	   @createTime: 2021-10-01 05:20:33
	   @arg: int* handles 
	   @arg:  char* status 
	   @arg:  int len 
	   @rtn: 
*/
	for(int i = 0; i < len; i++)
		write(handles[i], status + i, 1);
}

int wait(float delay, int* mutex)
{
	/*
	 * 延时等待函数。根据delay的大小（单位：秒）进行延时。
	 * 如果指针mutex对应的数据有变化，则提前结束等待。
	   @createTime: 2021-10-01 05:20:39
	   @arg: float delay 待等待的秒数
	   @arg:  int* mutex 监控的数据。如果变化则会提前结束函数。
	   @rtn: 
*/
	int tmp_mutex = *mutex;
	for(int i = 0 ; i < 100 * 100 * 40 * delay; i++){
		if(*mutex != tmp_mutex){
			return 1;
		}
	}
	return 0;
}

void thread_func_read_btn(void* btn_handles)
{
	/*
	 * 此函数由读按钮状态的线程使用。
	 * 不断循环，读按钮1, 2对应设备的状态，并更新全局变量gi32_led_status
	 * 来指示led灯显示的状态。另一个线程会使用gi32_led_status来控制led的显示。
	   @createTime: 2021-10-01 05:20:42
	   @arg: void* btn_handles 按键对应的句柄
	   @rtn: NULL
*/
	btn_handles = (int*)(btn_handles);
	char btn_status[NUM_BTNS] = {0};
	while(1) {
		read_status(btn_handles, btn_status, NUM_BTNS);
		if(btn_status[0] == BTN_PRESSED and btn_status[1] == BTN_PRESSED){
			gi32_led_status = 0;
		}
		else if(btn_status[0] == BTN_PRESSED and btn_status[1] == BTN_LOOSE){
			gi32_led_status = 1;
		}
		else if(btn_status[0] == BTN_LOOSE and btn_status[1] == BTN_PRESSED){
			gi32_led_status = 2;
		}
		else{
			gi32_led_status = 3;
		}
	}
}

void thread_func_write_led(void* led_handles)
{
	/*
	 * 此函数运行在一个线程中，根据全局变量gi32_led_status来
	 * 控制四个LED灯的输出状态。
	   @createTime: 2021-10-01 05:20:46
	   @arg: void* led_handles led灯的句柄
	   @rtn: NULL
*/
	led_handles = (int*)(led_handles);
	char led_status[NUM_LEDS] = {LED_ON};
	int tmp_status = 0;
	while(1){
		// 状态0，所有LED灯亮。
		if(gi32_led_status == 0){
			for(int i = 0; i < NUM_LEDS; i++){
				led_status[i] = LED_ON;
			}
			write_status(led_handles, led_status, NUM_LEDS);
		}

		// 状态1，LED灯隔一个为一组，两组轮流亮
		// 假定1为亮，0为灭。e.g: 0101 -> 1010 -> 0101 -> ...
		// 交替变换时间间隔：1.0秒
		else if(gi32_led_status == 1){
			led_status[0] = LED_ON;
			led_status[1] = LED_OFF;
			led_status[2] = LED_ON;
			led_status[3] = LED_OFF;
			write_status(led_handles, led_status, NUM_LEDS);
			if(wait(1.0, &gi32_led_status)){
				goto loop_end;

			}

			led_status[0] = LED_OFF;
			led_status[1] = LED_ON;
			led_status[2] = LED_OFF;
			led_status[3] = LED_ON;
			write_status(led_handles, led_status, NUM_LEDS);
			if(wait(1.0, &gi32_led_status)){
				goto loop_end;
			}
		}
		// 状态2，流水灯，LED灯按顺序轮流亮
		// 假定1为亮，0为灭。e.g: 1000 -> 0100 -> 0010 -> 0001 -> 1000 -> ...
		// 交替变换时间间隔：0.5秒
		else if(gi32_led_status == 2){
			led_status[0] = LED_ON;
			led_status[1] = LED_OFF;
			led_status[2] = LED_OFF;
			led_status[3] = LED_OFF;
			write_status(led_handles, led_status, NUM_LEDS);
			if(wait(0.5, &gi32_led_status)){
				goto loop_end;
			}

			led_status[0] = LED_OFF;
			led_status[1] = LED_ON;
			write_status(led_handles, led_status, NUM_LEDS);
			if(wait(0.5, &gi32_led_status)){
				goto loop_end;
			}

			led_status[1] = LED_OFF;
			led_status[2] = LED_ON;
			write_status(led_handles, led_status, NUM_LEDS);
			if(wait(0.5, &gi32_led_status)){
				goto loop_end;
			}

			led_status[2] = LED_OFF;
			led_status[3] = LED_ON;
			write_status(led_handles, led_status, NUM_LEDS);
			if(wait(0.5, &gi32_led_status)){
				goto loop_end;
			}
		}
		// 状态3，所有LED灯灭。
		else if(gi32_led_status == 3){
			for(int i = 0; i < NUM_LEDS; i++){
				led_status[i] = LED_OFF;
			}
			write_status(led_handles, led_status, NUM_LEDS);
		}
loop_end:

		if(gi32_led_status != tmp_status){
			tmp_status = gi32_led_status;
			printf("current status is: %d\n", tmp_status);
		}
	}
}

int main(int argc, char const *argv[])
{
	int return_val = 0;

	char led_device_base_name[] = "/dev/100ask_led0";
	char btn_device_base_name[] = "/dev/100ask_button0";

	// 获取设备句柄
	int* btn_handles = get_handles(btn_device_base_name, NUM_BTNS);
	int* led_handles = get_handles(led_device_base_name, NUM_LEDS);

	// 创建两个线程。一个用来读按钮的状态，一个根据按钮的状态来控制LED灯的输出。
	// 两个线程由变量gi32_led_status来通信。
	pthread_t thread_read_btn, thread_write_led;
	pthread_create(&thread_read_btn, NULL, (void*)(&thread_func_read_btn), (void*)(btn_handles));
	pthread_create(&thread_write_led, NULL, (void*)(&thread_func_write_led), (void*)(led_handles));

	// 如果线程正常结束，thread_join()会返回0
	// 有任何一个线程异常结束，则main函数不能返回0.
	if(pthread_join(thread_read_btn, NULL) or pthread_join(thread_write_led, NULL)){
		return_val = -1;
	}

	// 清空句柄资源。
	// 但是由于程序大概率是<Ctrl-c>强制退出的，也许根本不会执行到这里。
	close_handles(btn_handles, NUM_BTNS);
	close_handles(led_handles, NUM_LEDS);

	return return_val;
}

